<!--
 * @Description: 流程图预览
 * @Author: DHL
 * @Date: 2022-09-14 11:09:34
 * @LastEditors: DHL
 * @LastEditTime: 2023-07-13 09:26:58
-->
<template>
  <div class="preview-flowchart-wrapper">
    <div class="legend">
      <div class="square complete"></div>
      <div class="txt">已完成</div>

      <div class="square running"></div>
      <div class="txt">进行中</div>

      <div class="square unexecuted"></div>
      <div class="txt">未执行</div>
    </div>

    <div ref="canvasRef" class="canvas"></div>
  </div>
</template>

<script setup lang="ts">
  import BpmnViewer from 'bpmn-js/lib/NavigatedViewer'
  import { ElMessage } from 'element-plus'
  import { apiFlowProcessViewer } from '@/api/flow/flowProcess'

  //////////////////////////////////////////////////
  // 属性
  //////////////////////////////////////////////////
  const canvasRef = ref()

  const viewerModeler = ref()

  const defaultZoom = ref(1)

  const state = reactive({
    xmlStr: '',
    nodeList: [] as Array<any>
  })

  //////////////////////////////////////////////////
  // 函数
  //////////////////////////////////////////////////

  /**
   * 构建流程图
   * @param procInsId 流程实例
   */
  function build(params) {
    nextTick(() => {
      handleSearch(params)
    })
  }

  /**
   * 根据流程实例主键查询流程图
   */
  function handleSearch(params) {
    apiFlowProcessViewer(params).then((result) => {
      if (result?.success) {
        const { xmlStr, nodeList } = result.data

        state.xmlStr = xmlStr || ''
        state.nodeList = nodeList || []

        handleBuildBpmnViewer()
      } else {
        ElMessage.error(result?.message)
      }
    })
  }

  /**
   * 构建BpmnViewer
   */
  async function handleBuildBpmnViewer() {
    if (viewerModeler.value) {
      viewerModeler.value.destroy()
    }

    viewerModeler.value = new BpmnViewer({
      container: canvasRef.value
    })

    await viewerModeler.value.importXML(state.xmlStr)

    handleProcessReZoom()

    handleFillColor()
  }

  /**
   * 重置视图并居中
   */
  function handleProcessReZoom() {
    defaultZoom.value = 1
    viewerModeler.value.get('canvas').zoom('fit-viewport', 'auto')
  }

  /**
   * 设置高亮颜色的
   */
  function handleFillColor() {
    const canvas = viewerModeler.value.get('canvas')
    const nodeList = state.nodeList
    const nodeMap = new Map()
    let todoTask: any = {}
    nodeList.forEach((task) => {
      nodeMap.set(task.key, task)
      if (!task.completed) {
        todoTask = task
      }
    })

    function addMarkerWithColor(elementId, color) {
      canvas.addMarker(elementId, color)
    }

    function findNodeByKey(key) {
      return nodeMap.get(key)
    }

    function isTaskCompleted(task) {
      return task && task.completed
    }

    function addTaskMarker(task, color) {
      if (task) {
        addMarkerWithColor(task.key, color)
      }
    }

    viewerModeler.value.getDefinitions().rootElements[0].flowElements.forEach((element) => {
      if (element.$type === 'bpmn:UserTask') {
        const completeTask = findNodeByKey(element.id)

        if (completeTask) {
          console.info(completeTask)
          addTaskMarker(
            completeTask,
            isTaskCompleted(completeTask) ? 'highlight' : 'highlight-todo'
          )

          element.outgoing?.forEach((sequenceFlow) => {
            const targetTask = findNodeByKey(sequenceFlow.targetRef.id)

            if (targetTask) {
              const markerColor =
                isTaskCompleted(todoTask) &&
                completeTask.key === todoTask.key &&
                !todoTask.completed
                  ? isTaskCompleted(todoTask)
                    ? 'highlight'
                    : 'highlight-todo'
                  : isTaskCompleted(targetTask)
                  ? 'highlight'
                  : 'highlight-todo'

              addMarkerWithColor(sequenceFlow.id, markerColor)
              addMarkerWithColor(sequenceFlow.targetRef.id, markerColor)
            }
          })
        }
      } else if (
        element.$type === 'bpmn:ExclusiveGateway' ||
        element.$type === 'bpmn:ParallelGateway'
      ) {
        const completeTask = findNodeByKey(element.id)

        if (completeTask) {
          addTaskMarker(
            completeTask,
            isTaskCompleted(completeTask) ? 'highlight' : 'highlight-todo'
          )

          element.outgoing?.forEach((sequenceFlow) => {
            const targetTask = findNodeByKey(sequenceFlow.targetRef.id)

            if (targetTask) {
              addMarkerWithColor(
                sequenceFlow.id,
                isTaskCompleted(targetTask) ? 'highlight' : 'highlight-todo'
              )
              addMarkerWithColor(
                sequenceFlow.targetRef.id,
                isTaskCompleted(targetTask) ? 'highlight' : 'highlight-todo'
              )
            }
          })
        }
      } else if (element.$type === 'bpmn:StartEvent') {
        element.outgoing.forEach((sequenceFlow) => {
          const completeTask = findNodeByKey(sequenceFlow.targetRef.id)

          if (completeTask) {
            addMarkerWithColor(sequenceFlow.id, 'highlight')
            addMarkerWithColor(element.id, 'highlight')
            return
          }
        })
      } else if (element.$type === 'bpmn:EndEvent') {
        const endTask = nodeList[nodeList.length - 1]

        if (endTask && endTask.key === element.id && endTask.completed) {
          addMarkerWithColor(element.id, 'highlight')
        }
      }
    })
  }

  //////////////////////////////////////////////////
  // 生命周期
  //////////////////////////////////////////////////

  onMounted(() => {})

  onBeforeUnmount(() => {
    if (viewerModeler.value) {
      viewerModeler.value.destroy
    }
  })

  //////////////////////////////////////////////////
  // 暴露出去，父组件才可以调用到
  //////////////////////////////////////////////////
  defineExpose({
    build
  })
</script>

<style scoped lang="scss">
  // 已办
  $--done: #19be6b;
  // 待办
  $--todo: #ff9900;

  // 退回
  $--back: #fa3534;

  // 未执行
  $--unexecuted: rgb(34 36 42);

  .preview-flowchart-wrapper {
    width: 100%;
    height: 100%;
    min-width: 850px;
    min-height: 650px;

    :deep(.bjs-container) {
      .bjs-powered-by,
      .bjs-breadcrumbs {
        display: none;
      }

      // 节点
      .djs-shape {
        .djs-visual {
          > :nth-child(1) {
            fill-opacity: 0.2 !important;
          }

          .djs-label {
            fill-opacity: 1 !important;
          }
        }

        &.highlight {
          .djs-visual {
            > :nth-child(1) {
              // fill: $--done !important;
              stroke: $--done !important;
              fill-opacity: 0.2 !important;
            }

            .djs-label {
              fill: $--done !important;
            }

            path {
              // fill: $--done !important;
              fill-opacity: 0.2 !important;
              stroke: $--done !important;
            }
          }
        }

        &.highlight-todo {
          .djs-visual {
            > :nth-child(1) {
              // fill: $--todo !important;
              stroke: $--todo !important;
              stroke-dasharray: 4px !important;
              fill-opacity: 0.2 !important;
            }
          }
        }

        &.selected {
          .djs-outline {
            stroke: #4d90ff;
            stroke-width: 1px;
            stroke-dasharray: 3, 3;
          }
        }

        &.hover {
          .djs-outline {
            stroke: #4d90ff;
            stroke-width: 1px;
            stroke-dasharray: 3, 3;
          }
        }
      }

      // 连线
      .djs-connection {
        &.highlight {
          .djs-visual {
            path {
              stroke: $--done !important;
            }
          }
        }

        &.highlight-todo {
          .djs-visual {
            path {
              stroke: $--todo !important;
              stroke-dasharray: 4px !important;
              fill-opacity: 0.2 !important;
            }
          }
        }
      }

      .overlays-div {
        font-size: 10px;
        color: red;
        width: 100px;
        top: -20px !important;
      }
    }

    .legend {
      display: flex;
      justify-content: center;
      align-items: center;
      padding: 10px;

      .item {
        line-height: 22px;
      }

      .square {
        display: inline-block;
        width: 15px;
        height: 15px;
        border-radius: 3px;
      }

      .txt {
        padding: 0px 15px 0px 3px;
      }

      .complete {
        background-color: $--done;
      }

      .running {
        background-color: $--todo;
      }

      .unexecuted {
        background-color: $--unexecuted;
      }
    }
    .canvas {
      cursor: pointer;
      width: 100%;
      height: calc(100% - 45px);
    }
  }
</style>
